import { ref, reactive } from 'vue';
import { wss_url } from '@/config';
import { market } from "@/server/market";

// 市场状态
export const market_status = reactive({
    open: false,
    close: false,
});

export const chartDOM = {
    // k线 是否被创建
    is_created: false,
    // js里实例化的 k线 对象
    dom: null,
};

// 重置 k线 图表 监视属性会监听这个值的改变
export const klinechart_reset = ref('');

// 所有时间段
export const all_time = [
    { id: 1, name: '1Mins', shortName: '1M', value: 60 * 1000 },
    { id: 2, name: '5Mins', shortName: '5M', value: 5 * 60 * 1000 },
    { id: 3, name: '15Mins', shortName: '15M', value: 15 * 60 * 1000 },
    { id: 4, name: '30Mins', shortName: '30M', value: 30 * 60 * 1000 },
    { id: 5, name: '1Hour', shortName: '1H', value: 60 * 60 * 1000 },
    { id: 6, name: '1Day', shortName: '1D', value: 24 * 60 * 60 * 1000 },
    { id: 7, name: '1Week', shortName: '1W', value: 7 * 24 * 60 * 60 * 1000 },
    { id: 8, name: '1Month', shortName: '1Mon', value: 30 * 24 * 60 * 60 * 1000 },
];

// k线 时间段
export const select_time = ref(all_time[3].value);

// 所有 货币
export let all_symbols = [];

// 当前选中的的美股 默认读取 localStorage 里的 active_stock 读取不到则默认是 AAPL
export const active_stock = ref('AAPL.US');
// export const active_stock = ref(localStorage.getItem('active_stock') || 'AAPL.US');

// 当前订阅的k线数据
export const kline_data = ref([]);

// 下一根 k线 的时间戳
let next_kline_timestamp = 0;

// 修改股票函数
export function changeStock(symbol) {
    // 恢复到最开始的阶段 - websocket 断开后会自动重连的
    resetAllConnection();
    // 更新 股票
    active_stock.value = symbol;
}

// 修改k线周期的函数
export function changeTime(time) {
    // 恢复到最开始的阶段 - websocket 断开后会自动重连的
    resetAllConnection();
    // 更新 k线 时间段
    select_time.value = time;
}

function randomCreateSvgData(count) {
    let data = [];
    let res = '';
    let max, min = null;
    for (let i = 0; i < count; i++) {
        if (i) {
            let new_num = data[data.length - 1] + Math.floor(Math.random() * 5 - 2);
            if (new_num > max) max = new_num;
            if (new_num < min) min = new_num;
            data.push(new_num);
        } else {
            max = min = Math.floor(Math.random() * 50 - 25);
            data.push(max);
        }
    }
    let fenmu = max - min;
    for (let i = 0; i < data.length; i++) {
        res += ` ${ (i+1)*2 },${ Math.floor((data[i] - min) / fenmu * count) }`;
    }
    let svgStr = `<svg viewBox="0 0 ${ count*2 } ${ count }" xmlns="http://www.w3.org/2000/svg">
        <polyline points="${res.slice(1)}"
            fill="none"
            stroke="#22c55e"
            stroke-width="1"
            stroke-linecap="round"
            stroke-linejoin="round"/>
        </svg>`;
    return `data:image/svg+xml;base64,${ btoa(svgStr) }`
}

// 所有 美股 的列表
export const stock_list = ref([
    // {
    //     id: item.id,
    //     symbol: item.symbol,
    //     name: item.fullName,
    //     shortName: item.shortName,
    //     country: item.country,
    //     exchange: item.exchange,
    //     minTrade: item.minTrade,
    //     type: item.type,
    //     tradeStatus: item.tradeStatus,
    //     price: '$0.00',
    //     change: '+0.00%',
    //     volume: 0,
    //     market_cap: '0.00B',
    //     more: [
    //         {
    //             id: 1,
    //             k: 'Previous Close',
    //             v: '$0.00',
    //         },
    //         {
    //             id: 2,
    //             k: 'Open',
    //             v: '$0.00',
    //         },
    //         {
    //             id: 3,
    //             k: 'Day Range',
    //             v: '0.00 - 0.00',
    //         },
    //         {
    //             id: 4,
    //             k: 'Volume',
    //             v: '0.00'
    //         }
    //     ]
    // };
]);

export const marketService = {
    async getMarketStatus() {
        let res = await market.getMarketStatus();
        if (res.success) {
            if (res.data.code == 200) {
                market_status.open = res.data.data.open;
                market_status.close = res.data.data.close;
            } else {
                toast.warning('Market Failed! Please try again later!');
            }
        } else {
            toast.warning('Market Failed! Please try again later!');
        }
    },
    async getMarketList() {
        let res = await market.getMarketList();
        if (res.success) {
            if (res.data.code == 200) {
                let list = res.data.data.list;
                let new_list = list.map((item) => {
                    return {
                        id: item.id,
                        symbol: item.symbol,
                        name: item.fullName,
                        shortName: item.shortName,
                        country: item.country,
                        exchange: item.exchange,
                        minTrade: item.minTrade,
                        type: item.type,
                        tradeStatus: item.tradeStatus,
                        price: '$0.00',
                        change: '+0.00%',
                        volume: 0,
                        market_cap: '0.00B',
                        svg: randomCreateSvgData(50),
                        more: [
                            {
                                id: 1,
                                k: 'Previous Close',
                                v: '$0.00',
                            },
                            {
                                id: 2,
                                k: 'Open',
                                v: '$0.00',
                            },
                            {
                                id: 3,
                                k: 'Day Range',
                                v: '0.00 - 0.00',
                            },
                            {
                                id: 4,
                                k: 'Volume',
                                v: '0.00'
                            }
                        ]
                    };
                });
                stock_list.value = new_list;
                all_symbols = new_list.map(item => item.symbol);
            } else {
                toast.warning('Market List Failed! Please try again later!');
            }
        } else {
            toast.warning('Market List Failed! Please try again later!');
        }
    },
    resetKLineChart() {
        // 恢复到最开始的阶段
        resetAllConnection();
        // 将两个 websocket 连接初始化
        history_ws.init();
        realtime_ws.init();
    },
};

// 初始化 历史k线 WebSocket 连接
/*
    取消订阅最新k线
    {"id":5,"method":"kline.unsubscribe","params":[]}

    持续查询历史k线
    {"id":6,"method":"kline.query","params":["BTCUSDT","60"]}

    {"id":2,"method":"today.subscribe","params":["BTCUSDT"]}
    {"id":4,"method":"kline.subscribe","params":["BTCUSDT","60"]}
*/
class HistoryKlineWebSocket {
    constructor() {
        // id = '' 表示此时没有 ws连接
        this.id = '';
        this.ws = null;
        this.interval = null;
    }

    init() {
        try {
            // 创建一个 WebSocket 实例
            if (this.id) return ;
            this.id = Date.now().toString(36) + Math.random().toString(36).slice(2);
            // 连接 WebSocket
            console.log(`正在连接 ${ this.id } ...`);
            this.ws = new WebSocket(wss_url);
            // 事件绑定
            this.ws.onopen = this.onopen.bind(this);
            this.ws.onmessage = this.onmessage.bind(this);
            this.ws.onerror = this.onerror.bind(this);
            this.ws.onclose = this.onclose.bind(this);
        } catch (e) {
            console.error(`${ this.id } 连接失败: ${ e.message }`, e);
            this.id = '';
            if (this.ws) { this.ws.close(); this.ws = null; }
            if (this.interval) { clearInterval(this.interval); this.interval = null; }
        }
    }

    onopen(event) {
        console.log(`HistoryKlineWebSocket ${ this.id } 连接已建立`, this, this.ws);
        // 查询历史 k线 由于这里用到了 active_stock 和 select_time 也就是说 连接这个 ws 之前必须确保这俩对象是有数据的
        this.sendMessage(JSON.stringify({ id: 6, method: 'kline.query', params: [ active_stock.value, `${ select_time.value / 1000 }` ] }));
        // 订阅最新k线 由于这里用到了 active_stock 和 select_time 也就是说 连接这个 ws 之前必须确保这俩对象是有数据的
        this.sendMessage(JSON.stringify({ id: 4, method: "kline.subscribe", params: [ active_stock.value, `${ select_time.value / 1000 }` ] }));
        // 定时发送心跳包
        this.interval = setInterval(() => {
            this.sendMessage('1');
        }, 5000);
    }

    onmessage(event) {
        // 接收数据
        let data = JSON.parse(event.data);
        // 丢弃心跳包
        if (typeof data == 'number') return ;
        // 处理数据
        if (data.id == 6) {
            this.handleHistoryKlineData(data);
        }
    }

    onerror(event) {
        console.error(`${ this.id } 发生错误: ${ event }`);
    }

    onclose(event) {
        console.log(`${ this.id } 连接已关闭 (代码: ${ event.code }, 原因: ${ event.reason || '无' })`, event);
        // 清除数据
        this.id = '';
        if (this.interval) { clearInterval(this.interval); this.interval = null; }
        // 100ms 后重新连接
        setTimeout(() => {
            this.init();
        }, 100);
    }

    sendMessage(s) {
        if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
            console.error('未建立连接或连接未就绪');
            return;
        }

        if (!s) {
            console.error('消息内容不能为空');
            return;
        }

        try {
            this.ws.send(s);
            console.log(`HistoryKlineWebSocket ${ this.id } 已发送: ${ s }`);
        } catch (error) {
            console.error(`发送失败: ${ error.message }`);
        }
    }

    closeConnection() {
        console.log(`断开 ${ this.id } 连接`);
        this.id = '';
        // 停掉定时器
        if (this.interval) {
            clearInterval(this.interval);
            this.interval = null;
        }
        // 关闭 websocket
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.close();
            this.ws = null;
        }
    }

    /**
     * 这个方法必须保证 kline_data 在切换 股票 或者 周期 的时候 必须是空的
     * next_kline_timestamp 用来记录最后一条 k线 的时间戳
     * active_stock 用来记录当前选中的股票 必须要有值才行
     * select_time 用来记录当前选中的周期 必须要有值才行
     */
    handleHistoryKlineData(data) {
        let new_kline_data = [];
        for (let i = 0; i < data.result.length; i++) {
            let item = data.result[i];
            new_kline_data.push({
                timestamp: item[0] * 1000,
                open: item[1],
                close: item[2],
                high: item[3],
                low: item[4],
                volume: item[5]
            });
        }
        console.log('这次返回了 ' + new_kline_data.length + ' 条数据');
        // TODO: 存储下这组k线数据 并想办法通知 kchart 重新渲染
        kline_data.value.unshift(...new_kline_data);
        // 触发监视属性 klinechart_reset
        klinechart_reset.value = Date.now().toString(36) + Math.random().toString(36).slice(2);
        // 存储下一条k线的数据
        if (!next_kline_timestamp) next_kline_timestamp = parseInt(data.result[data.result.length - 1][0]) * 1000;
        // 限制k线数量为 3000 条数据
        if (data.result.length >= 300 && kline_data.value.length < 3000) {
            this.sendMessage(JSON.stringify({ id: 6, method: 'kline.query', params: [ active_stock.value, `${ select_time.value / 1000 }`, String(data.result[0][0]) ] }));
        } else {
            console.log('数据已全部获取');
        }
    }
}

/*
 * 取消订阅产品信息
 * {"id":3,"method":"untoday.subscribe","params":[]}
 * 
 * 订阅产品信息
 * {"id":2,"method":"today.subscribe","params":["XAUUSD","XAGUSD"]}
 */
class RealTimeKlineWebSocket {
    constructor() {
        // id = '' 表示此时没有 ws连接
        this.id = '';
        this.ws = null;
        this.interval = null;
    }

    init() {
        try {
            // 创建一个 WebSocket 实例
            if (this.id) return ;
            this.id = Date.now().toString(36) + Math.random().toString(36).slice(2);
            // 连接 WebSocket
            console.log(`正在连接 ${ this.id } ...`);
            this.ws = new WebSocket(wss_url);
            // 事件绑定
            this.ws.onopen = this.onopen.bind(this);
            this.ws.onmessage = this.onmessage.bind(this);
            this.ws.onerror = this.onerror.bind(this);
            this.ws.onclose = this.onclose.bind(this);
        } catch (e) {
            console.error(`${ this.id } 连接失败: ${ e.message }`, e);
            this.id = '';
            if (this.ws) { this.ws.close(); this.ws = null; }
            if (this.interval) { clearInterval(this.interval); this.interval = null; }
        }
    }

    onopen(event) {
        // 这个方法必须保证 all_symbols 不为空
        console.log(`RealTimeKlineWebSocket ${ this.id } 连接已建立`, this, this.ws);
        // 订阅产品信息 - 后续操作写在事件监听里
        this.sendMessage(JSON.stringify({ id: 2, method: 'today.subscribe', params: all_symbols }));
        // 定时发送心跳包
        this.interval = setInterval(() => {
            this.sendMessage('1');
        }, 5000);
    }

    onmessage(event) {
        // 接收数据
        let data = JSON.parse(event.data);
        // 丢弃心跳包
        if (typeof data == 'number') return ;
        // 处理数据
        if (data.method == 'today.update') {
            this.handleTodayUpdateData(data);
        } else if (data.method == 'price.update') {
            this.handlePriceUpdateData(data);
        }
    }

    onerror(event) {
        console.error(`${ this.id } 发生错误: ${ event }`);
    }

    onclose(event) {
        console.log(`${ this.id } 连接已关闭 (代码: ${ event.code }, 原因: ${ event.reason || '无' })`, event);
        // 清除数据
        this.id = '';
        if (this.interval) { clearInterval(this.interval); this.interval = null; }
        // 100ms 后重新连接
        setTimeout(() => {
            this.init();
        }, 100);
    }

    sendMessage(s) {
        if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
            console.error('未建立连接或连接未就绪');
            return;
        }

        if (!s) {
            console.error('消息内容不能为空');
            return;
        }

        try {
            this.ws.send(s);
            console.log(`RealTimeKlineWebSocket ${ this.id } 已发送: ${ s }`);
        } catch (error) {
            console.error(`发送失败: ${ error.message }`);
        }
    }

    closeConnection() {
        console.log(`断开 ${ this.id } 连接`);
        this.id = '';
        // 停掉定时器
        if (this.interval) {
            clearInterval(this.interval);
            this.interval = null;
        }
        // 关闭 websocket
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.close();
            this.ws = null;
        }
    }

    handleTodayUpdateData(data) {
        let symbol = data.result.symbol;
        console.log('收到今日数据:', data);
        // 寻找对应的货币（ TODO: 未来要优化到 O(1) ）
        for (let i = 0; i < stock_list.value.length; i++) {
            if (stock_list.value[i].symbol == symbol) {
                stock_list.value[i].price = `$${ parseFloat(data.result.last ?? 0).toFixed(2) }`;
                stock_list.value[i].volume = data.result.volume ?? 0;
                stock_list.value[i].more[0].v = `$${ parseFloat(data.result.last ?? 0).toFixed(2) }`;
                stock_list.value[i].more[1].v = `$${ parseFloat(data.result.open ?? 0).toFixed(2) }`;
                stock_list.value[i].more[2].v = `${ parseFloat(data.result.low ?? 0).toFixed(2) } - ${ parseFloat(data.result.high ?? 0).toFixed(2) }`;
                stock_list.value[i].more[3].v = `$${ parseFloat(data.result.volume ?? 0).toFixed(2) }`;
                console.log('更新完毕', stock_list.value[i]);
                break;
            }
        }
    }

    handlePriceUpdateData(data) {
        // 最新一笔交易的价格
        let new_price = parseFloat(data.price);
        let new_price_format = new_price.toFixed(2);
        // 更新所有币种价格
        for (let i = 0; i < stock_list.value.length; i++) {
            if (stock_list.value[i].symbol == data.symbol) {
                let open = parseFloat(stock_list.value[i].more[1].v.slice(1));
                stock_list.value[i].price = `$${ new_price_format }`;
                let new_change = `${ parseFloat((new_price - open) / open * 100).toFixed(2) }%`;
                new_change = (new_change.slice(0, 1) != '-' ? '+' : '') + new_change;
                // TODO: 暴力避免 -0.00% 的出现 未来需要查清楚原因
                if (new_change === '-0.00%' || new_change === '0.00%') new_change = '+0.00%';
                stock_list.value[i].change = new_change;
                break;
            }
        }
        if (data.symbol == active_stock.value) {
            // 更新下一根 k线 的数据
            let new_ts = parseInt(data.ts) * 1000;
            // 确保时间戳是递增的
            if (next_kline_timestamp == 0) next_kline_timestamp = new_ts;
            // 如果 最新一根k线 的时间戳 小于 下一根k线 的时间戳
            if (new_ts - next_kline_timestamp < select_time.value) {
                // 获取最后一条k线的数据
                let obj = kline_data.value[kline_data.value.length - 1];
                // 切换 时间 币种 会导致拿不到最后一条 k线
                if (obj) {
                    obj.close = new_price;
                    // 判断是否超过最高价
                    if (obj.high < new_price) {
                        obj.high = new_price;
                    }
                    // 判断是否超过最低价
                    if (obj.low > new_price) {
                        obj.low = new_price;
                    }
                    // 更新 k线
                    if (chartDOM.is_created) {
                        chartDOM.dom.updateData(obj);
                    }
                    kline_data.value[kline_data.value.length - 1] = obj;
                }
            } else {
                // 更新最新k线的时间戳
                next_kline_timestamp += select_time.value;
                // 构建新的 k线
                let obj = {
                    timestamp: next_kline_timestamp,
                    open: new_price,
                    close: new_price,
                    high: new_price,
                    low: new_price,
                    volume: 1,
                };
                // 添加新的k线
                if (chartDOM.is_created) {
                    chartDOM.dom.updateData(obj);
                }
                kline_data.value.push(obj);
            }
        }
    }
}

// 关闭所有 websocket 连接 - websocket会自动重连的
export function closeAllConnection() {
    history_ws.closeConnection();
    realtime_ws.closeConnection();
}

// 关闭所有 websocket 连接并将数据恢复到最开始的阶段
export function resetAllConnection() {
    closeAllConnection();
    kline_data.value = [];
    next_kline_timestamp = 0;
}

// websocket 实例对象
export let history_ws = new HistoryKlineWebSocket();     // 历史k线 websocket 实例对象
export let realtime_ws = new RealTimeKlineWebSocket();   // 实时k线 websocket 实例对象
